package com.gitee.wsl.compose.modifier.input

import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.FloatAnimationSpec
import androidx.compose.animation.core.FloatTweenSpec
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.input.pointer.pointerInput
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds


fun Modifier.onHoldTouch(
    easing: Easing = FastOutSlowInEasing,
    pollDelay: Duration = 50.milliseconds,
    targetPollDelay: Duration = pollDelay,
    animationDuration: Duration = 3.seconds,
    onTouchHeld: () -> Unit,
) = composed {
    val scope = rememberCoroutineScope()

    pointerInput(Unit) {
        val animationSpec: FloatAnimationSpec = FloatTweenSpec(duration = animationDuration.inWholeMilliseconds.toInt(), delay = 0, easing = easing)

        awaitEachGesture {
            val initialDown = awaitFirstDown(requireUnconsumed = false)
            val initialTouchHeldJob = scope.launch {
                var currentPlayTime = 0.milliseconds
                var delay = pollDelay
                while (initialDown.pressed) {
                    onTouchHeld()

                    delay(delay.inWholeMilliseconds)
                    currentPlayTime += delay
                    delay = animationSpec.getValueFromNanos(
                        playTimeNanos = currentPlayTime.inWholeNanoseconds,
                        initialValue = pollDelay.inWholeMilliseconds.toFloat(),
                        targetValue = targetPollDelay.inWholeMilliseconds.toFloat(),
                        initialVelocity = 0F,
                    ).toInt().milliseconds
                }
            }
            waitForUpOrCancellation()
            initialTouchHeldJob.cancel()
        }
    }
}
